#include "stdafx.h"
#include "Prog2.h"

#include "MainFrm.h"
#include "Prog2Doc.h"
#include "Prog2View.h"
#include "zeroinit.h"
#include "EphemWrapper.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CProg2View

IMPLEMENT_DYNCREATE(CProg2View, CView)

BEGIN_MESSAGE_MAP(CProg2View, CView)
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
END_MESSAGE_MAP()


CProg2View::CProg2View()
: m_pointBeginSelection(-1, -1)
{
	m_selectedRect.m_nStyle = PS_DASH;
	m_GridParam = zero_init<CGridParam>();

	if(!m_font.CreatePointFont(80, _T("Lucida Console")))
		throw CCustomException (_T("CDrawer::CDrawer: Create font failed"));
}

CProg2View::~CProg2View()
{
}

void CProg2View::OnInitialUpdate()
{
	CView::OnInitialUpdate();
	{
		CProg2Doc* pDoc = GetDocument();
		ASSERT_VALID(pDoc);
		pDoc->GetInitialGridParam(m_GridParam);
	}
	m_ThreadReDraw.StartThread(this, &CProg2View::ReDrawFunc, true, false);
}


BOOL CProg2View::PreCreateWindow(CREATESTRUCT& cs)
{
	return CView::PreCreateWindow(cs);
}

// CProg2View drawing

void CProg2View::OnDraw(CDC* pDC)
{
	CRect rcClient;
	GetClientRect(rcClient);

	CDC dcMemory;
	dcMemory.CreateCompatibleDC(pDC);
	{
		CAutoCriticalSection asc(m_csDrawingBitmap);
		CBitmap* pOldBitmap = dcMemory.SelectObject(m_phDrawingBitmap.get());
		pDC->BitBlt(rcClient.left,	rcClient.top, rcClient.Width(),	rcClient.Height(),	&dcMemory, 	0,	0,	SRCCOPY);
		dcMemory.SelectObject(pOldBitmap);
	}

	if(m_lineMouse.IsIncluded(rcClient))
	{
		m_lineMouse.Draw(*pDC);
		m_lineMouse.Clean();
	}

	if(m_selectedRect.IsIncluded(rcClient))
	{
		m_selectedRect.Draw(*pDC);
		//m_selectedRect.Clean();
	}
}


// CProg2View printing

BOOL CProg2View::OnPreparePrinting(CPrintInfo* pInfo)
{
	return DoPreparePrinting(pInfo);
}

void CProg2View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
}

void CProg2View::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{
}


// CProg2View diagnostics

#ifdef _DEBUG
void CProg2View::AssertValid() const
{
	CView::AssertValid();
}

void CProg2View::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CProg2Doc* CProg2View::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CProg2Doc)));
	return (CProg2Doc*)m_pDocument;
}
#endif //_DEBUG



void CProg2View::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);

	CClientDC hDC(this);
	CDC dcMemory;
	dcMemory.CreateCompatibleDC(&hDC);

	CAutoCriticalSection asc(m_csDrawingBitmap);
	m_phDrawingBitmap = CBitmapPtr( new CBitmap );
	m_phDrawingBitmap->CreateCompatibleBitmap( &hDC, cx, cy);
	
	if(1)
	{
		CBitmap* pOldBitmapDst = dcMemory.SelectObject(m_phDrawingBitmap.get());
		COLORREF colorDef = RGB(150, 150, 150);
		CBrush brush;
		brush.CreateSolidBrush(colorDef);
		dcMemory.FillRect( CRect(0, 0, cx, cy), &brush);
		dcMemory.SelectObject(pOldBitmapDst);
	}
	RequestRedraw();
}

void CProg2View::RequestRedraw()
{
	m_evntRedraw.SetEvent();
}

BOOL CProg2View::OnEraseBkgnd(CDC* pDC)
{
	//return CView::OnEraseBkgnd(pDC);
	return TRUE;
}


unsigned CProg2View::ReDrawFunc(CEvent& evtTerminate)
{
	DWORD dwTimeout = INFINITE;
	HANDLE hEvent[] = {evtTerminate, m_evntRedraw};
	while (1)
	{
		DWORD dwRes = WaitForMultipleObjects(sizeof(hEvent)/sizeof(hEvent[0]), hEvent, false, dwTimeout);
		switch(dwRes)
		{
		case WAIT_OBJECT_0: 
			return 0;
		case WAIT_OBJECT_0 + 1: 
			dwTimeout = 100;
			break;
		case WAIT_TIMEOUT:
			if(!ReDrawAll())
				return 0;
			Invalidate(FALSE);
			dwTimeout = INFINITE;
			break;
		default:
			_ASSERT(0);
			Sleep(100);
			break;
		}
	}
	return 0;
}

bool CProg2View::ReDrawAll()
{
	CClientDC hDC(this);

	CDC dcMemoryDst;
	dcMemoryDst.CreateCompatibleDC(&hDC);

	CBitmapPtr pLocalBitmap;
	{
		CAutoCriticalSection asc(m_csDrawingBitmap);
		pLocalBitmap = m_phDrawingBitmap;
	}
	
	BITMAP BitMap;
	pLocalBitmap->GetBitmap(&BitMap);

	CBitmap* pOldBitmapDst = dcMemoryDst.SelectObject(pLocalBitmap.get());

	{ // clean bgrnd
		COLORREF colorDef = RGB(255, 255, 255);
		CBrush brush;
		brush.CreateSolidBrush(colorDef);
		dcMemoryDst.FillRect( CRect(0, 0, BitMap.bmWidth, BitMap.bmHeight), &brush);
	}

	DrawGrid(dcMemoryDst, BitMap.bmWidth, BitMap.bmHeight);
	DrawApproachs(dcMemoryDst, BitMap.bmWidth, BitMap.bmHeight);

	dcMemoryDst.SelectObject(pOldBitmapDst);
	return true;
}

bool CProg2View::DrawGrid(CDC &cdc, int nWidth, int nHeight)
{
	COLORREF colorBgrnd = RGB(0, 0, 0);

	if(nWidth < 0 || nHeight < 0)
		return true;

	{
		CPen penDef;
		penDef.CreatePen(PS_DOT, 1, colorBgrnd);
		CPen * pOldDefPen = cdc.SelectObject(&penDef);

		{
			int nTickCount = 10;
			for(int i = 0; i <= nTickCount; i++)
			{
				int nYpos = i * nHeight/nTickCount;
				if (i != nTickCount && i != 0)
				{
					CPoint pt(0, nYpos );
					cdc.MoveTo(pt);
					pt.x = nWidth;
					cdc.LineTo(pt);
				}
			}
		}

		{
			int nTickCount = 10;
			CStdString strText;
			for(int i = 0; i <= nTickCount; i++)
			{
				int nXpos = i * nWidth/nTickCount;
				if (i != 0 && i != nTickCount)
				{
					CPoint pt(nXpos, 0 );
					cdc.MoveTo(pt);
					pt.y = nHeight;
					cdc.LineTo(pt);
				}
			}
		}

		cdc.SelectObject(pOldDefPen);
	}
	return true;
}

bool CProg2View::DrawApproachs(CDC &cdc, int nWidth, int nHeight)
{
	ListPtrApproachs ListApprchs;
	{
		CProg2Doc* pDoc = GetDocument();
		//ASSERT_VALID(pDoc);
		if (!pDoc)
			return false;
		pDoc->GetApproachesInRange(m_GridParam, ListApprchs);
	}

	m_ApproachHolder.Clean();
	m_ApproachHolder.Load(ListApprchs, m_GridParam);
	m_ApproachHolder.Draw(&cdc, nWidth, nHeight);
	return true;
}


void CProg2View::OnMouseMove(UINT nFlags, CPoint point)
{
	if( nFlags & MK_LBUTTON )
	{
		if(m_pointBeginSelection.x == -1 && m_pointBeginSelection.y == -1)
			m_pointBeginSelection = point;
		m_selectedRect.m_TopLeft = m_pointBeginSelection;
		m_selectedRect.m_RightBottom = point;
		//HideToolTip();
	}
	else
	{
		m_pointBeginSelection.x = -1;
		m_pointBeginSelection.y = -1;
		SearchNearestApp(point);
	}

	{ // status bar text
		CStdString strX;
		CStdString strY;
		if(	m_GridParam.IsValid() )
		{
			CRect rcClient;
			GetClientRect(rcClient);

			double dx = m_GridParam.getXVal( rcClient.Width(), point.x);
			double dy = m_GridParam.getYVal(rcClient.Height(), point.y);

			int year, month, date;
			double daypart;
			CEphemWrapper::DateFromJD(dy , year, month, date, daypart);
			strX.Format(_T("%-20.15lf (%-20.15lf km)"), dx, CEphemWrapper::AUtoKM(m_GridParam.GetParamDiff(dx)));
			strY.Format(_T("%04d-%02d-%02d.%02d"), year, month, date, int(daypart*100));
		}
		else
		{
			strX.Format(_T("%ld"), point.x);
			strY.Format(_T("%ld"), point.y);
		}

		CStdString strText;
		strText.Format(_T("%s | %s"), strX.c_str(), strY.c_str());

		CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
		pMainFrame->SetStatusText(strText);
	}

	Invalidate(FALSE);
	CView::OnMouseMove(nFlags, point);
}

void CProg2View::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_selectedRect.Clean();
	Invalidate(FALSE);

	CView::OnLButtonUp(nFlags, point);
}

bool CProg2View::SearchNearestApp(CPoint point)
{

	double dist = -1.0;

	CRect rcClient;
	GetClientRect(rcClient);
	//int nHeight = rcClient.Height();
	//int nWidth = rcClient.Width();

	double dX = point.x / double(rcClient.Width());
	double dY = point.y / double(rcClient.Height());
	double dDist = 0.0;
	CApproachDrawer::Ptr pResAppDrawer = m_ApproachHolder.GetNearest(dX, dY, &dDist);
	if( pResAppDrawer && dDist < 0.05)
	{
		CPoint pointRes = pResAppDrawer->GetPos(rcClient.Width(), rcClient.Height());
		
		CApproach::Ptr pResApp = pResAppDrawer->GetApproach();
		CStdString str;
		int year, month, date;
		double daypart;
		CEphemWrapper::DateFromJD(pResApp->m_dTime , year, month, date, daypart);
		daypart += date;
		str.Format(_T("%s\n%04d-%02d-%09.6lf\n%lf !km\n%-20.15lf\n%-20.15lf !km"),
			CEphemWrapper::GetBodyName(pResApp->m_bodyNum), 
			year, month, daypart, 
			CEphemWrapper::AUtoKM(pResApp->Dist()),
			pResApp->m_pAsteroidOrbitParam->GetVariousParam(),
			CEphemWrapper::AUtoKM(m_GridParam.GetParamDiff(pResApp->m_pAsteroidOrbitParam->GetVariousParam())));
	
		ShowToolTip(point, str);

		m_lineMouse.m_TopLeft = pointRes;
		m_lineMouse.m_RightBottom = point;
	}
	else
	{
		HideToolTip();
	}
	return true;
}

bool CProg2View::ShowToolTip(CPoint point, LPCTSTR stzText)
{
	DWORD dwOffset = 3;
	int nWndShift = 8;
	CRect rcClient;
	GetClientRect(rcClient);

	if(m_pWndToolTip.get() == NULL)
	{
		DWORD dwStyle = WS_POPUP | WS_BORDER; 
		DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
		m_pWndToolTip = auto_ptr<CWnd> (new CWnd());
		CString lpszClassName = AfxRegisterWndClass(/*CS_VREDRAW | CS_HREDRAW*/ 0, LoadCursor(NULL, IDC_ARROW), m_brushToolTipBkgd);
		if (!m_pWndToolTip->CreateEx(dwExStyle, lpszClassName, NULL, dwStyle, 0, 0, 0, 0, NULL, NULL))
		{
			_ASSERT(0);
			m_pWndToolTip.reset();
			return FALSE;
		}
		m_prevPoint = CPoint(0, 0);
	}
	if(m_bToolTipShown && m_prevPoint == point)
		return true;

	m_prevPoint = point;
	ClientToScreen(&point);
	HWND hForegroundWnd = ::GetForegroundWindow();

	CClientDC dc(m_pWndToolTip.get());
	CFont *pOldFont = dc.SelectObject(&m_font);
	CRect rc(0, 0, 0, 0);
	dc.DrawText(stzText, -1, &rc, DT_CALCRECT | DT_LEFT | DT_NOPREFIX);
	CRect rectWnd = rc;
	rectWnd.top -= dwOffset;
	rectWnd.bottom += dwOffset;
	rectWnd.right += dwOffset;

	if(m_prevPoint.x > rcClient.Width() / 2)
		point.x -= rectWnd.Width() + nWndShift;
	else
		point.x += nWndShift;

	if(m_prevPoint.y > rcClient.Height() / 2)
		point.y -= rectWnd.Height() + nWndShift;
	else
		point.y += nWndShift;

	m_pWndToolTip->MoveWindow(point.x, point.y, rectWnd.Width(), rectWnd.Height());
	m_pWndToolTip->ShowWindow(SW_NORMAL);

	//m_pWndToolTip->SetWindowPos(pWnd, pt.x, pt.y-rectWnd.Height(), rectWnd.Width(), rectWnd.Height(), SWP_NOACTIVATE| SWP_SHOWWINDOW);

	dc.FillRect( rectWnd, &m_brushToolTipBkgd);
	dc.DrawText(stzText, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_VCENTER);
	dc.SelectObject(&pOldFont);
	m_bToolTipShown = TRUE;

	if(hForegroundWnd)
		::SetForegroundWindow(hForegroundWnd);
	return true;
}

bool CProg2View::HideToolTip()
{
	if(!m_bToolTipShown)
		return TRUE;

	if(m_pWndToolTip.get())
	{
		m_pWndToolTip->ShowWindow(SW_HIDE);
	}
	m_bToolTipShown = FALSE;
	return TRUE;
}

bool CProg2View::AppendTraceApproaches(CApproachTrace::Ptr listApprc)
{
	ListPtrApproachs listPtrApproaches;
	listApprc->GetApproachesList(m_GridParam, listPtrApproaches);
	ListPtrApproachs::const_iterator it = listPtrApproaches.begin();
	ListPtrApproachs::const_iterator end = listPtrApproaches.end();
	for(; it!=end; ++it)
	{
		m_ApproachHolder.Append(*it);
	}
	RequestRedraw();
	return true;
}